//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#define __NO_LINKNODE_CONSTRUCTOR
#include <core.h>
#include <mantle.h>
#include "peimage.h"
#include <crust.h>
#include <_pubcrt.h>
#include "loader.h"

typedef struct PeInfo {
    UInt32        uImageBase;
    UInt32        uEntryPoint;
    UInt32        cSections;
    UInt32        uTotalSize;

    UInt32        uExportRVA;
    UInt32        uExportSize;
    UInt32        uImportRVA;
    UInt32        uImportSize;
    UInt32        uResourceRVA;
    UInt32        uResourceSize;

    UInt32        uRelocOffset;
    UInt32        uRelocSize;
    UInt32        uDebugInfoRVA;

    UInt32        uSecHeaderOffset;
}   PeInfo;

static DLinkNode s_moduleList = {
    &s_moduleList, &s_moduleList
};

#ifdef DEBUG_KMUTEX
static KMutex s_loaderLock(__FILE__, __LINE__);
#else
static KMutex s_loaderLock;
#endif //DEBUG_KMUTEX

ECode LoadPeModule(
        const wchar_t *wszName,
        UInt32 dwFlags,
        ModuleFileType  modFileType,
        CModule * pModule);

ECode ParsePeHeader(
        CLoadedFile *pFile,
        ModuleFileType  modFileType,
        PeInfo * pInfo);

ECode LoadPeSections(
        CLoadedFile *pFile,
        PeInfo * pInfo,
        CModule * pModule);

ECode RelocPeModule(
        CModule * pModule,
        UInt32 uOffset,
        PIMAGE_BASE_RELOCATION pReloc,
        UInt32 uRelocSize);

ECode FixupExport(
        CModule * pModule,
        UInt32 uImageBase);

ECode FixupImport(
        CModule * pModule,
        UInt32 uImageBase);

EXTERN ECode GetBootModule(
    const wchar_t *wszName, virtaddr_t *pvaBase, virtaddr_t *pvaLimit);

inline CModule * FindModule(const wchar_t *wszName)
{
    CModule * pModule;
    wchar_t *wszFullPath;
    wchar_t *pPathEnv;
    wchar_t *pPos;
    ECode ec;
    _ELASTOS Boolean bEnd = FALSE;
    int nlen;

    ec = E_FILE_NOT_FOUND;
    wszFullPath = new wchar_t[_MAX_PATH];
    if (!wszFullPath) return NULL;
    wszFullPath[0] = L'\0';

    if (!wcsstr(wszName, L"\\") && !wcsstr(wszName,L"/")) {
        ec = GetBootModule(wszName, NULL, NULL);
        if (ec == NOERROR) {
            wcscpy(wszFullPath, L"C:/");
            wcscat(wszFullPath, wszName);
        }
    }

    if (ec != NOERROR) {
        if (FAILED(WFullPath(wszFullPath, wszName, _MAX_PATH))) {
            wszFullPath[0] = L'\0';
        }
    }

    if (wszFullPath[0] != L'\0') {
        ForEachDLinkNode(CModule *, pModule, &s_moduleList) {
            if (!_wcsicmp(wszFullPath, pModule->m_wszFullPath)) {
                DzDisablePreemption();
                if (0 != pModule->m_cRef) {
                    pModule->AddRef();
                    EnablePreemption();
                    delete wszFullPath;
                    return pModule;
                }
                EnablePreemption();
            }
        }

        if (IS_PATH_SEPARATOR(wszName[0]) || wszName[1] == L':') {
            delete wszFullPath;
            return NULL;
        }
    }

    pPathEnv = WGetEnv(L"PATH");
    if (pPathEnv == NULL) {
        delete wszFullPath;
        return NULL;
    }

    while (!bEnd) {
        if ((pPos = wcschr(pPathEnv, L';')) != NULL) {
            nlen = pPos - pPathEnv;
            nlen = ((nlen < _MAX_PATH) ? nlen : _MAX_PATH);
            wcsncpy(wszFullPath, pPathEnv, nlen);
            wszFullPath[nlen] = L'\0';

            if (*(pPos + 1) != L'\0')
                pPathEnv = pPos + 1;
            else
                bEnd = TRUE;
        }
        else {
            wcsncpy(wszFullPath, pPathEnv, _MAX_PATH);
            wszFullPath[_MAX_PATH] = L'\0';
            bEnd = TRUE;
        }

        if (wcslen(wszFullPath) + wcslen(wszName) + 1 >= _MAX_PATH) continue;
        wcscat(wszFullPath, WCS_PATH_SEPARATOR);
        wcscat(wszFullPath, wszName);
        if (FAILED(WFullPath(wszFullPath, wszFullPath, _MAX_PATH))) continue;
        ForEachDLinkNode(CModule *, pModule, &s_moduleList) {
            if (!_wcsicmp(wszFullPath, pModule->m_wszFullPath)) {
                DzDisablePreemption();
                if (0 != pModule->m_cRef) {
                    pModule->AddRef();
                    EnablePreemption();
                    delete wszFullPath;
                    return pModule;
                }
                EnablePreemption();
            }
        }
    }

    delete wszFullPath;
    return NULL;
}

#define ENTER_LOADER()  s_loaderLock.Lock()
#define EXIT_LOADER(r)  do { s_loaderLock.Unlock(); return r; } while(0)

EXTERN_C wchar_t *   __cdecl  _wgetdcwd( int drive, wchar_t *buffer, int maxlen );

void AnsiToUnic(wchar_t *pwszName, const char *pszName);
ECode DoVerifyFile(const wchar_t *wszName);

ECode GLoadModuleA(
        const char *szName,
        UInt32 dwFlags,
        ModuleFileType  modFileType,
        CModule ** ppModule)
{
    wchar_t *wszName;

    wszName = (wchar_t *)malloc((strlen(szName) + 1) * sizeof(wchar_t));

    if (!wszName) {
        return E_OUT_OF_MEMORY;
    }

    AnsiToUnic(wszName, szName);

    ECode ec = GLoadModuleW(wszName, dwFlags, modFileType, ppModule);
    free(wszName);
    return ec;
}

ECode GLoadModuleW(
        const wchar_t *wszName,
        UInt32 dwFlags,
        ModuleFileType  modFileType,
        CModule ** ppModule)
{
    CModule * pModule = NULL;
    ECode ec;

    ENTER_LOADER();

    *ppModule = FindModule(wszName);
    if (*ppModule) {
        EXIT_LOADER(NOERROR);
    }

    ec = DoVerifyFile(wszName);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    pModule = new CModule;
    if (!pModule) {
        EXIT_LOADER(E_OUT_OF_MEMORY);
    }
    s_moduleList.InsertNext(pModule);

    ec = LoadPeModule(wszName, dwFlags, modFileType, pModule);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    *ppModule = pModule;
    pModule->AddRef();

    EXIT_LOADER(NOERROR);

ErrorExit:
    if (pModule) {
        GUnloadModule(pModule);
        delete pModule;
    }
    EXIT_LOADER(ec);
}

void GUnloadModule(
    CModule * pModule)
{
    ENTER_LOADER();

    assert (0 == pModule->m_cRef);

    if (pModule->m_wszFullPath) delete pModule->m_wszFullPath;
    if (pModule->m_pSections) delete [] pModule->m_pSections;
    if (pModule->m_uImageBase &&
        !FALL_INTO(pModule->m_uImageBase, EXE_IMAGE_BASE, EXE_IMAGE_SIZE)) {
        GlobalAS::FreeB(pModule->m_uImageBase, pModule->m_uTotalSize);
    }

    pModule->Detach();
    s_loaderLock.Unlock();
}

ECode LoadPeModule(
        const wchar_t *wszName,
        UInt32 dwFlags,
        ModuleFileType  modFileType,
        CModule * pModule)
{
    PeInfo peInfo;
    ECode ec;
    CLoadedFile file;

    ec = file.Init(wszName, pModule);
    if (FAILED(ec))
        goto ErrorExit;

    ec = ParsePeHeader(&file, modFileType, &peInfo);
    if (NOERROR != ec) {
        goto ErrorExit;
    }

    if (ModuleFileType_EXE == modFileType) {
        pModule->m_uImageBase = peInfo.uImageBase;

        if (EXE_IMAGE_SIZE < peInfo.uTotalSize) {
            ec = E_NOT_ENOUGH_ADDRESS_SPACE;
            goto ErrorExit;
        }

        if (!FALL_INTO(peInfo.uImageBase, EXE_IMAGE_BASE, EXE_IMAGE_SIZE)) {
            if (NULL == peInfo.uRelocOffset) {
                ec = E_BAD_EXE_FORMAT;
                goto ErrorExit;
            }
            else {
                pModule->m_uImageBase = EXE_IMAGE_BASE;
            }
        }

        if (!FALL_INTO(peInfo.uImageBase + peInfo.uTotalSize,
                       EXE_IMAGE_BASE, EXE_IMAGE_SIZE)) {
            if (NULL == peInfo.uRelocOffset) {
                ec = E_NOT_ENOUGH_ADDRESS_SPACE;
                goto ErrorExit;
            }
            else {
                pModule->m_uImageBase = EXE_IMAGE_BASE;
            }
        }
    }
    else if (ModuleFileType_DLL == modFileType
             && NULL == peInfo.uRelocOffset) {
        if (!FALL_INTO(peInfo.uImageBase, DLL_IMAGE_BASE, DLL_IMAGE_SIZE)) {
            ec = E_BAD_DLL_FORMAT;
            goto ErrorExit;
        }
        if (!FALL_INTO(peInfo.uImageBase + peInfo.uTotalSize,
                       DLL_IMAGE_BASE, DLL_IMAGE_SIZE)) {
            ec = E_NOT_ENOUGH_ADDRESS_SPACE;
            goto ErrorExit;
        }

        // Alloc fixed address space
        pModule->m_uImageBase = GlobalAS::AllocFixedB(peInfo.uImageBase, peInfo.uTotalSize);
        if (!pModule->m_uImageBase) {
            ec = E_NOT_ENOUGH_ADDRESS_SPACE;
            goto ErrorExit;
        }
    }
    else {
        pModule->m_uImageBase = GlobalAS::AllocB(peInfo.uTotalSize);
        if (!pModule->m_uImageBase) {
            ec = E_NOT_ENOUGH_ADDRESS_SPACE;
            goto ErrorExit;
        }
    }


    pModule->m_uTotalSize = peInfo.uTotalSize;
    pModule->m_uEntryPoint = peInfo.uEntryPoint + pModule->m_uImageBase;
    pModule->m_uRelocOffset = pModule->m_uImageBase - peInfo.uImageBase;

    ec = LoadPeSections(&file, &peInfo, pModule);
    if (NOERROR != ec) {
        goto ErrorExit;
    }

    if (pModule->m_uRelocOffset && peInfo.uRelocSize) {
        PIMAGE_BASE_RELOCATION pRlc = NULL;

        ec = file.Read(peInfo.uRelocOffset, peInfo.uRelocSize,
                                 (PVoid*)&pRlc);
        if (FAILED(ec)) {
            goto ErrorExit;
        }

        ec = RelocPeModule(
            pModule,
            pModule->m_uRelocOffset,
            pRlc,
            peInfo.uRelocSize);
        if (NOERROR != ec) {
            goto ErrorExit;
        }
    }

    if (pModule->m_pExport) {
        ec = FixupExport(pModule, pModule->m_uImageBase);
        if (NOERROR != ec) {
            goto ErrorExit;
        }
    }

    if (pModule->m_pImport) {
        ec = FixupImport(pModule, pModule->m_uImageBase);
        if (NOERROR != ec) {
            goto ErrorExit;
        }
    }

    // free unused sections
    Int32 n;
    for (n = pModule->m_cSections - 1; n >= 0 ; n--) {
        if (!strcmp(pModule->m_pSections[n].m_szName, ".reloc")) {
            pModule->m_pSections[n].Section::~Section();
            new (&pModule->m_pSections[n]) Section;
            break;
        }
    }

#ifdef _ELADBGAGENT
    if (peInfo.uDebugInfoRVA) {
        PIMAGE_DEBUG_DIRECTORY pDebugDir = \
                (PIMAGE_DEBUG_DIRECTORY)((PByte)(pModule->m_uImageBase) + peInfo.uDebugInfoRVA);
        pModule->m_pImgDebugDirEntry = (PByte)pDebugDir;
    }
#endif

    ec = NOERROR;

ErrorExit:

    return ec;
}

ECode ParsePeHeader(
        CLoadedFile *pFile, ModuleFileType modFileType,
        PeInfo * pInfo)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_FILE_HEADER pFileHeader = NULL;
    PIMAGE_OPTIONAL_HEADER pOptHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    ECode ec = NOERROR;
    Int32 lNewHeaderOffset;
    UInt32 uSize = pFile->GetSize();

    // Verify DOS and PE magic number
    //
    if (uSize < sizeof(IMAGE_DOS_HEADER)) {
        kprintf("*ERROR* Not PE file\n");
        return E_BAD_FILE_FORMAT;
    }

    ec = pFile->Read(0, sizeof(IMAGE_DOS_HEADER),
                               (PVoid*)&pDosHeader);

    if (FAILED(ec))
        goto ErrorExit;

    if (pDosHeader->uhMagic != IMAGE_DOS_SIGNATURE
        || pDosHeader->lNewHeaderOffset < 0
        || (UInt32)pDosHeader->lNewHeaderOffset > uSize
        || (sizeof(IMAGE_NT_HEADERS) + (UInt32)pDosHeader->lNewHeaderOffset)
            > uSize) {
        kprintf("*ERROR* Not PE file\n");
        ec = E_BAD_FILE_FORMAT;
        goto ErrorExit;
    }

    lNewHeaderOffset = pDosHeader->lNewHeaderOffset;
    ec = pFile->Read(lNewHeaderOffset
            , sizeof(IMAGE_NT_HEADERS), (PVoid*)&pNTHeader);
    if (FAILED(ec))
        goto ErrorExit;

    if (pNTHeader->ulSignature != IMAGE_NT_SIGNATURE) {
        kprintf("*ERROR* Not PE file\n");
        ec = E_BAD_FILE_FORMAT;
        goto ErrorExit;
    }

    // Check File-Header
    //
    pFileHeader = &pNTHeader->fileHeader;

#ifdef _x86
    if (pFileHeader->uhMachine != IMAGE_FILE_MACHINE_I386 &&
        pFileHeader->uhMachine != IMAGE_FILE_MACHINE_I386 + 1 &&
        pFileHeader->uhMachine != IMAGE_FILE_MACHINE_I386 + 2)
#elif defined(_arm)
    if (pFileHeader->uhMachine != IMAGE_FILE_MACHINE_ARM &&
        pFileHeader->uhMachine != IMAGE_FILE_MACHINE_ARM + 2)
#elif defined(_mips)
    if (pFileHeader->uhMachine != IMAGE_FILE_MACHINE_R4000)
#else
#error Unknown architecture
#endif
    {
        kprintf("Not support the executable file.\n");
        ec = (ModuleFileType_DLL == modFileType) \
            ? E_BAD_DLL_FORMAT : E_BAD_EXE_FORMAT;
        goto ErrorExit;
    }

    if (pFileHeader->uhSizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER)
        || !(pFileHeader->uhCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE)
        || (pFileHeader->uhCharacteristics & ModuleFileType_MARK) != modFileType
        ) {
        ec = (ModuleFileType_DLL == modFileType) \
            ? E_BAD_DLL_FORMAT : E_BAD_EXE_FORMAT;
        goto ErrorExit;
    }


    // Check Optional-Header
    //
    pOptHeader = &pNTHeader->optionalHeader;
    if (pOptHeader->uhMagic != 0x010b) {
        kprintf("*ERROR* Invalid Optional Header of PE file\n");
        ec = (ModuleFileType_DLL == modFileType) \
            ? E_BAD_DLL_FORMAT : E_BAD_EXE_FORMAT;
        goto ErrorExit;
    }

    //
    // Get summary info of this image.
    //
    pInfo->cSections = pFileHeader->uhNumberOfSections;
    pInfo->uImageBase = pOptHeader->ulImageBase;
    pInfo->uEntryPoint = pOptHeader->ulAddressOfEntryPoint;
    pInfo->uTotalSize = pOptHeader->ulSizeOfImage;

    pInfo->uExportRVA = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].ulVirtualAddress;
    pInfo->uExportSize = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].ulSize;
    pInfo->uImportRVA = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].ulVirtualAddress;
    pInfo->uImportSize = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].ulSize;

    pInfo->uResourceRVA = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].ulVirtualAddress;
    pInfo->uResourceSize = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].ulSize;
    pInfo->uRelocOffset = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].ulVirtualAddress;
    pInfo->uRelocSize = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].ulSize;

    pInfo->uSecHeaderOffset = lNewHeaderOffset + sizeof(IMAGE_NT_HEADERS);

    pInfo->uDebugInfoRVA = pOptHeader-> \
        aDataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].ulVirtualAddress;

    ec = NOERROR;

ErrorExit:

    return ec;
}

ECode LoadPeSections(
        CLoadedFile *pFile,
        PeInfo * pInfo,
        CModule * pModule)
{
    ECode ec = NOERROR;
    Boolean bNeedContMem;
    register UInt32 n;
    register PIMAGE_SECTION_HEADER pSecHeader;
    Byte * pSecHeaderData;

    pModule->m_cSections = pInfo->cSections;
    pModule->m_pSections = new Section[pModule->m_cSections];
    if (!pModule->m_pSections) {
        return E_OUT_OF_MEMORY;
    }

    for (UInt32 i = 0; i < pModule->m_cSections; i++) {
        pModule->m_pSections[i].Initialize(pModule);
    }

    pSecHeaderData = (Byte *)DzAllocDiscontinuousKernelPages(
            AlignPageUp(pInfo->cSections * sizeof(IMAGE_SECTION_HEADER))
                / PAGE_SIZE, 0);
    if (pSecHeaderData == NULL) {
        return E_OUT_OF_MEMORY;
    }

    ec = pFile->Read(pInfo->uSecHeaderOffset,
            pInfo->cSections * sizeof(IMAGE_SECTION_HEADER),
            (PVoid*)&pSecHeaderData, FALSE);
    if (FAILED(ec))
        goto EXIT;

    pSecHeader = (PIMAGE_SECTION_HEADER)pSecHeaderData;
    for (n = 0; n < pModule->m_cSections; n++) {
        memcpy(pModule->m_pSections[n].m_szName, pSecHeader->szName, 8);
        pModule->m_pSections[n].SetAddr(pSecHeader->ulVirtualAddress);
        pModule->m_pSections[n].SetSize(pSecHeader->misc.ulVirtualSize);

        pModule->m_pSections[n].m_uPointerToRawData = pSecHeader->ulPointerToRawData;
        pModule->m_pSections[n].m_uSizeOfRawData = pSecHeader->ulSizeOfRawData;

        //
        // Get protect attributes of section.
        //
        if (pSecHeader->ulCharacteristics & IMAGE_SCN_MEM_READ) {
            pModule->m_pSections[n].m_dwProtect |= MemoryProtection_Read;
        }
        if (pSecHeader->ulCharacteristics & IMAGE_SCN_MEM_WRITE) {
            pModule->m_pSections[n].m_dwProtect |= MemoryProtection_Write;
        }
        if (pSecHeader->ulCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
            pModule->m_pSections[n].m_dwProtect |= MemoryProtection_Exec;
        }

        //
        // Check current section, if it's an especial section such as
        // relocation, export, import or resource, we need record it's info
        // in module.
        //
        bNeedContMem = FALSE;
        if (pSecHeader->ulVirtualAddress == pInfo->uRelocOffset) {
            pInfo->uRelocOffset = pSecHeader->ulPointerToRawData;
        }
        if (FALL_INTO(pInfo->uResourceRVA,
            pSecHeader->ulVirtualAddress, pSecHeader->misc.ulVirtualSize)) {
            pModule->m_pResource = &(pModule->m_pSections[n]);

            if (!strcmp((const char*)pSecHeader->szName, ".rsrc")
                || !strcmp((const char*)pSecHeader->szName, ".idata")) {
                // BUGBUG: GNU compiler make resource section and idata section writeable.
                // Clear writeable flag to save memory.
                pModule->m_pSections[n].m_dwProtect &= ~MemoryProtection_Write;
            }
        }
        // The export table
        if (FALL_INTO(pInfo->uExportRVA,
            pSecHeader->ulVirtualAddress, pSecHeader->misc.ulVirtualSize)) {
            assert(NULL == pModule->m_pExport);
            pModule->m_pExport = &(pModule->m_pSections[n]);
            pModule->m_uExportOffset =
                pInfo->uExportRVA - pSecHeader->ulVirtualAddress;
            pModule->m_uExportSize = pInfo->uExportSize;
            bNeedContMem = TRUE;
            // Clear writeable flag to save memory AND catch invalid pointer.
            pModule->m_pSections[n].m_dwProtect &= ~MemoryProtection_Write;
        }
        // The import table
        if (FALL_INTO(pInfo->uImportRVA,
            pSecHeader->ulVirtualAddress, pSecHeader->misc.ulVirtualSize)) {
            assert(NULL == pModule->m_pImport);
            pModule->m_pImport = &(pModule->m_pSections[n]);
            pModule->m_uImportOffset =
                pInfo->uImportRVA - pSecHeader->ulVirtualAddress;
            pModule->m_uImportSize = pInfo->uImportSize;
            bNeedContMem = TRUE;
            // Clear writeable flag to save memory AND catch invalid pointer.
            pModule->m_pSections[n].m_dwProtect &= ~MemoryProtection_Write;
        }

#if (_TEST_TYPE == 3) || (_TEST_TYPE == 4) \
    || (_TEST_TYPE == 5) || (_TEST_TYPE == 6) ||(_TEST_TYPE == 1000)
/*Test for performance*/
        if (1) {
#else
        if ((pModule->m_uRelocOffset && pInfo->uRelocSize) || bNeedContMem
            || !_wcsnicmp(pModule->m_wszFullPath, L"C:\\", 3)
            || !_wcsnicmp(pModule->m_wszFullPath, L"C:/", 3)
            || pFile->HasProxy()) {
#endif

            // No raw data
            if (!pSecHeader->ulPointerToRawData || !pSecHeader->ulSizeOfRawData) {
                pModule->m_pSections[n].m_pFile = NULL;
            }
            else {
                //
                // Allocate physical memory for section.
                //
                ec = pModule->m_pSections[n].MemAlloc(bNeedContMem);
                if (FAILED(ec)) goto EXIT;

                //
                // Initialize the content of the section.
                //
                pModule->m_pSections[n].MemZero();
                if (pSecHeader->ulPointerToRawData && pSecHeader->ulSizeOfRawData) {
                    UInt32 uLmt = pSecHeader->ulSizeOfRawData;
                    UInt32 uOff = 0;

                    while (uOff < uLmt) {
                        Byte *pRawData;
                        UInt32 uRead = MIN(MIN_BUFFER_SIZE, uLmt - uOff);

                        ec = pFile->Read(pSecHeader->ulPointerToRawData + uOff,
                                         uRead, (PVoid*)&pRawData);
                        if (FAILED(ec)) goto EXIT;

                        pModule->m_pSections[n].MemCopy(uOff, uRead, pRawData);
                        uOff += uRead;
                    }
                    assert(uOff == uLmt);
                }
            }
        }
        else {
            pModule->m_pSections[n].m_pFile = new CLoadedFile();
            if (!pModule->m_pSections[n].m_pFile) {
                ec = E_OUT_OF_MEMORY;
                goto EXIT;
            }
            ec = pModule->m_pSections[n].m_pFile->Init(pModule->m_wszFullPath);
            if (FAILED(ec)) {
                delete pModule->m_pSections[n].m_pFile;
                pModule->m_pSections[n].m_pFile = NULL;
                goto EXIT;
            }
        }

        pSecHeader++;
    }

EXIT:
    DzFreeDiscontinuousKernelPages(pSecHeaderData);
    return ec;
}

ECode RelocPeModule(
        CModule * pModule,
        UInt32 uOffset,
        PIMAGE_BASE_RELOCATION pReloc,
        UInt32 uRelocSize)
{
    register UInt32 n, m, uRelocAddr, uType;

    while (pReloc->ulVirtualAddress != 0 && uRelocSize > 0) {
        m = 0;
        while (pReloc->ahTypeOffset[m] != 0 && \
            (((pReloc->ulSizeOfBlock - 8) >> 1) > m)) {
            // Get a RVA which need to be relocated from this relocating block.
            //
            uRelocAddr = pReloc->ulVirtualAddress +
                (pReloc->ahTypeOffset[m] & (0x0fff));
            uType = pReloc->ahTypeOffset[m] >> 12;

            // Find section which the RVA fell into, and fixup the RVA pointed
            // address.
            //
            for (n = 0; n < pModule->m_cSections; n++) {
                if (FALL_INTO(uRelocAddr,
                    pModule->m_pSections[n].m_uVAddr,
                    pModule->m_pSections[n].m_uVSize)) {
                    if (IMAGE_REL_BASED_HIGHADJ != uType) {
                        pModule->m_pSections[n].RelocAddr(
                        uRelocAddr, uOffset, uType);
                    }
                    else {
                        if (!pModule->m_pSections[n].RelocAddr(
                            uRelocAddr,
                            uOffset + (Int16)pReloc->ahTypeOffset[++m] + 0x8000,
                            uType)) {
                            return E_UNEXPECTED;
                        }
                    }

                    break;
                }
            }
            if (n == pModule->m_cSections) {
                return E_UNEXPECTED;
            }

            m++;
        }

        uRelocSize -= (Int32)pReloc->ulSizeOfBlock;
        pReloc = (PIMAGE_BASE_RELOCATION)
            ((Byte *)pReloc + pReloc->ulSizeOfBlock);
    }

    return NOERROR;
}

//
// To fixup export section, we need the section which contain export data
// must be in a continuing physical memory block, so we can mapped it in a
// continuing kernel address space and operated it as a C structure.
//
ECode FixupExport(
        CModule * pModule,
        UInt32 uImageBase)
{
    PIMAGE_EXPORT_DIRECTORY pExport;
    register UInt32 n, ** ppLocal, uExportRVA;

    pExport = (PIMAGE_EXPORT_DIRECTORY)pModule->GetExportKAddress();
    uExportRVA = pModule->m_pExport->m_uVAddr + pModule->m_uExportOffset;

    //
    // Fixup the exported functions' address.
    //
    ppLocal = (UInt32 **)((UInt32)pExport + \
        (UInt32)pExport->plAddressOfFunctions - uExportRVA);

    pExport->plAddressOfFunctions = (UInt32 **)ppLocal;
    for (n = 0; n < pExport->ulNumberOfFunctions; n++) {
        if (0 != ppLocal[n]) {
            ppLocal[n] = (UInt32 *)((UInt32)ppLocal[n] + uImageBase);
        }
    }

    //
    // Fixup the address of name of exported functions.  Those names' address
    // are fixuped in kernel space, because they do not need to be operated
    // directly by an user process.
    //
    ppLocal = (UInt32 **)((UInt32)pExport + \
        (UInt32)pExport->plAddressOfNames - uExportRVA);
    pExport->plAddressOfNames = (UInt32 **)ppLocal;


    for (n = 0; n < pExport->ulNumberOfNames; n++) {
        if (0 != ppLocal[n]) {
            ppLocal[n] = (UInt32 *)((UInt32)pExport + (UInt32)ppLocal[n] - uExportRVA);
        }
    }

    //
    // The ordinals base address is fixuped up in kernel space too.
    //
    pExport->phAddressOfNameOrdinals = (UInt16 *)((UInt32)pExport + \
        (UInt32)pExport->phAddressOfNameOrdinals - uExportRVA);

    return NOERROR;
}

UInt32 GetExportAddressByOrdinal(
        CModule * pModule,
        UInt32 uOrdinal)
{
    PIMAGE_EXPORT_DIRECTORY pExport;

    if (NULL == pModule->m_pExport) return 0;

    pExport = (PIMAGE_EXPORT_DIRECTORY)pModule->GetExportKAddress();
    uOrdinal -= pExport->ulBase;

    if (uOrdinal < pExport->ulNumberOfFunctions) {
        return (UInt32)(pExport->plAddressOfFunctions[uOrdinal]);
    }
    else {
        return 0;
    }
}

UInt32 GetExportAddressByName(
        CModule * pModule,
        const char * pszName)
{
    PIMAGE_EXPORT_DIRECTORY pExport;
    UInt32 n;
    char ** ppNames;

    if (NULL == pModule->m_pExport) return 0;

    pExport = (PIMAGE_EXPORT_DIRECTORY)pModule->GetExportKAddress();
    ppNames = (char **)pExport->plAddressOfNames;

    for (n = 0; n < pExport->ulNumberOfNames; n++) {
        if (!strcmp(pszName, ppNames[n])) {
            n = pExport->phAddressOfNameOrdinals[n];
            return (UInt32)(pExport->plAddressOfFunctions[n]);
        }
    }
    return 0;
}

ECode FixupImport(
        CModule * pModule,
        UInt32 uImageBase)
{
    PIMAGE_IMPORT_DESCRIPTOR pImport;
    PIMAGE_THUNK_DATA pOrgThunk, pFirstThunk;
    PIMAGE_IMPORT_BY_NAME pImportName;
    CModule * pExpModule;
    ModuleRef * pModuleRef;
    ECode ec;
    char * pszDllName, *pImportBase;
    UInt32 uFunction, uImportRVA;

    pImport = (PIMAGE_IMPORT_DESCRIPTOR)pModule->GetImportKAddress();
    pImportBase = (char *)pImport;
    uImportRVA = pModule->m_pImport->m_uVAddr + pModule->m_uImportOffset;

    while (pImport->ulOriginalFirstThunk != 0) {
        //
        // Get the dependent DLL's name and try to load it.
        //
        pszDllName = pImportBase + pImport->ulName - uImportRVA;
        pImport->ulName = pImport->ulName + uImageBase;

        ec = GLoadModuleA(pszDllName, 0, ModuleFileType_DLL, &pExpModule);
        if (NOERROR != ec) {
            kprintf("ERROR: The dynamic link library %s could not be found.\n",
                pszDllName);
            return ec;
        }

        pModuleRef = new ModuleRef(pExpModule);
        if (!pModuleRef) {
            pExpModule->Release();
            return E_OUT_OF_MEMORY;
        }
        pModule->m_rModuleList.InsertPrev(pModuleRef);

        // Get and fixup the base address of orginial and first thunk to
        // kernel space.
        //
        pOrgThunk = (PIMAGE_THUNK_DATA)(pImport->ulOriginalFirstThunk);
        pFirstThunk = pImport->pFirstThunk;
        pOrgThunk = (PIMAGE_THUNK_DATA)((UInt32)pImportBase + \
            (UInt32)pOrgThunk - uImportRVA);

        for (UInt32 n = 0; n < pModule->m_cSections; n++) {
            if (FALL_INTO((UInt32)pFirstThunk,
                pModule->m_pSections[n].m_uVAddr,
                pModule->m_pSections[n].m_uVSize)) {

                pFirstThunk = (IMAGE_THUNK_DATA *)(
                (UInt32)__VA(
                ((PPage *)(pModule->m_pSections[n].m_cache.m_pagesList.m_pNext))->m_uAddr)
                     + (UInt32)pFirstThunk - pModule->m_pSections[n].m_uVAddr);
                break;
            }
        }


        while (*(PUInt32)pOrgThunk != 0) {
            if (IMAGE_SNAP_BY_ORDINAL(pOrgThunk->u1.ulOrdinal)) {
                uFunction = GetExportAddressByOrdinal(
                    pExpModule,
                    IMAGE_ORDINAL(pOrgThunk->u1.ulOrdinal));
                if (!uFunction) {
                    kprintf("Load \"%S\" failed! - Ordinal Not Found:\n"
                       "The ordinal %d could not be located "
                       "in the dynamic link library \"%S\".\n",
                       pModule->m_wszName,
                       IMAGE_ORDINAL(pOrgThunk->u1.ulOrdinal),
                       pExpModule->m_wszName);
                }
            }
            else {
                pImportName = pOrgThunk->u1.pAddressOfData;
                pImportName = (PIMAGE_IMPORT_BY_NAME)((UInt32)pImportBase + \
                    (UInt32)pImportName - uImportRVA);
                uFunction = GetExportAddressByName(
                    pExpModule,
                    (char *)pImportName->acName);
                if (!uFunction) {
                    kprintf("Load \"%S\" failed! - Entry Point Not Found:\n"
                      "The procedure entry point \"%s\" could not be located\n"
                      "in the dynamic link library \"%S\".\n",
                      pModule->m_wszName,
                      pImportName->acName,
                      pExpModule->m_wszName);
                }
            }
            if (!uFunction) {
                return E_DOES_NOT_EXIST;
            }
            pFirstThunk->u1.plFunction = (UInt32 *)uFunction;

            pOrgThunk++;
            pFirstThunk++;
        }

        pImport++;
    }

    return NOERROR;
}

